package org.quipu.client.charts;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import static com.google.gwt.user.client.ui.HasHorizontalAlignment.ALIGN_LEFT;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import static com.google.gwt.user.client.ui.HasVerticalAlignment.ALIGN_TOP;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.google.gwt.user.datepicker.client.DateBox;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.moxieapps.gwt.highcharts.client.Axis;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
import org.moxieapps.gwt.highcharts.client.Color;
import org.moxieapps.gwt.highcharts.client.Legend;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.ToolTip;
import org.moxieapps.gwt.highcharts.client.plotOptions.AreaSplinePlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
import org.quipu.client.AppCache;
import org.quipu.client.AppEventWidget;
import org.quipu.client.ControlChangeHandler;
import org.quipu.client.CurrencyListBox;
import org.quipu.client.DateTimeFormat;
import org.quipu.client.events.AccountChangedEvent;
import org.quipu.client.events.AppEvent;
import org.quipu.client.events.AppEventBus;
import org.quipu.client.events.AppEventListener;
import org.quipu.client.events.BookSelectionChangedEvent;
import org.quipu.client.events.LoginEvent;
import org.quipu.client.events.TransactionChangedEvent;
import org.quipu.dto.CurrencyDto;

public class BalanceChart extends AppEventWidget implements AppEventListener, ControlChangeHandler<CurrencyListBox, Long> {

    private final VerticalPanel vPanel;
    private final DateBox dateFromBox;
    private final DateBox dateToBox;
    private final CurrencyListBox currencyListBox;
    private CheckBox plannedCheckBox;
    private final Chart chart;
    private final DateTimeFormat dateTimeFormat = new DateTimeFormat("yyyy-MM-dd");

    public BalanceChart(AppEventBus eventBus, AppCache cache) {
        super(eventBus, cache);

        vPanel = new VerticalPanel();
        vPanel.addStyleName("controlPanel");
        vPanel.setHorizontalAlignment(ALIGN_LEFT);
        vPanel.setVerticalAlignment(ALIGN_TOP);
        vPanel.setSize("100%", "100%");

        dateFromBox = new DateBox();
        dateFromBox.addStyleName("dateInput");
        dateFromBox.setFormat(new DateBox.DefaultFormat(new DateTimeFormat("dd.MM.yyyy")));
        dateFromBox.setValue(new Date());
        dateFromBox.addValueChangeHandler(new ValueChangeHandler<Date>() {
            @Override
            public void onValueChange(ValueChangeEvent<Date> event) {
                updateChart();
            }
        });
        Date dateFrom = new Date();
        CalendarUtil.addDaysToDate(dateFrom, -30);
        dateFromBox.setValue(dateFrom);

        dateToBox = new DateBox();
        dateToBox.addStyleName("dateInput");
        dateToBox.setFormat(new DateBox.DefaultFormat(new DateTimeFormat("dd.MM.yyyy")));
        dateToBox.setValue(new Date());
        dateToBox.addValueChangeHandler(new ValueChangeHandler<Date>() {
            @Override
            public void onValueChange(ValueChangeEvent<Date> event) {
                updateChart();
            }
        });
        dateToBox.setValue(new Date());
        currencyListBox = new CurrencyListBox(eventBus, cache, null);
//        currencyListBox.

        plannedCheckBox = new CheckBox("учитывать запланированные");
        plannedCheckBox.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                updateChart();
            }
        });
        plannedCheckBox.setValue(false);

        HorizontalPanel hPanel = new HorizontalPanel();
        hPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
        hPanel.add(new Label("Период: "));
        hPanel.add(dateFromBox);
        hPanel.add(new Label("..."));
        hPanel.add(dateToBox);
        hPanel.add(new Label(" Валюта: "));
        hPanel.add(currencyListBox.getWidget());
        hPanel.add(plannedCheckBox);
        vPanel.add(hPanel);

        chart = new Chart();
        chart.setZoomType(Chart.ZoomType.X)
                .setSpacingRight(20)
                .setChartTitle(new ChartTitle().setText("Баланс"))
                .setToolTip(new ToolTip().setShared(true))
                .setLegend(new Legend().setEnabled(false))
                .setSize(700, 300);

        chart.getXAxis()
                .setType(Axis.Type.DATE_TIME)
                .setMaxZoom(7 * 24 * 3600000)
                .setAxisTitleText(null);

        chart.getYAxis()
                .setAxisTitleText("Баланс")
                .setStartOnTick(true);
        vPanel.add(chart);
    }

    public void updateChart() {
        if(getCache().getCurrentBook() != null && dateFromBox.getValue() != null
                && dateToBox.getValue() != null && currencyListBox.getValue() != null) {
            final Date dateFrom = dateFromBox.getValue();
            Date dateTo = dateToBox.getValue();
            boolean approvedOnly = (plannedCheckBox.getValue() == null? true: !plannedCheckBox.getValue());
            getMainService().getBalanceArchive(getCache().getCurrentBook().getId(), dateFrom, dateTo, approvedOnly, new AsyncCallback<List<Map<Long, Double[]>>>() {

                public void onSuccess(List<Map<Long, Double[]>> archive) {
                    Map<Long, Double[]> incomes = archive.get(0);
                    Map<Long, Double[]> outcomes = archive.get(1);
                    Map<Long, Double[]> balances = archive.get(2);
                    Double[] curIncomes = incomes.get(currencyListBox.getValue());
                    Double[] curOutcomes = outcomes.get(currencyListBox.getValue());
                    Double[] curBalances = balances.get(currencyListBox.getValue());
                    chart.removeAllSeries();

                    chart.addSeries(chart.createSeries()
                            .setType(Series.Type.AREA_SPLINE)
                            .setName("Доходы")
                            .setPlotOptions(setupPlotOptions(new AreaSplinePlotOptions(), 122, 146, 25, 0.3, 1, dateFrom))
                            .setPoints(curIncomes));
                    chart.addSeries(chart.createSeries()
                            .setType(Series.Type.AREA_SPLINE)
                            .setName("Расходы")
                            .setPlotOptions(setupPlotOptions(new AreaSplinePlotOptions(), 170, 70, 67, 0.3, 1, dateFrom))
                            .setPoints(curOutcomes));
                    chart.addSeries(chart.createSeries()
                            .setType(Series.Type.SPLINE)
                            .setName("Баланс")
                            .setPlotOptions(setupPlotOptions(new AreaSplinePlotOptions(), 69, 114, 167, 0.0, 2, dateFrom))
                            .setPoints(curBalances));
                    CurrencyDto cur = getCache().getCurrency(currencyListBox.getValue());
                    if(cur != null) {
                        chart.getYAxis().setAxisTitleText(cur.getTitle());
                        chart.setChartTitle(new ChartTitle().setText("Баланс, " + cur.getTitle()));
                    }
                }

                public void onFailure(Throwable caught) {
                    fireError("Failed to load balance chart: " + caught.getMessage());
                }
            });
        }
    }

    private AreaSplinePlotOptions setupPlotOptions(AreaSplinePlotOptions plotOptions, int r, int g, int b, Double alpha, int lineWidth, Date dateFrom) {
        plotOptions.setColor(new Color(r, g, b))
                .setMarker(new Marker()
                .setEnabled(false)
                .setHoverState(new Marker()
                    .setEnabled(true)
                    .setSymbol(Marker.Symbol.CIRCLE)
                    .setRadius(3)))
                .setPointInterval(24 * 3600 * 1000)
                .setPointStart(dateFrom.getTime())
                .setLineWidth(lineWidth)
                .setHoverStateLineWidth(lineWidth + 1)
                .setShadow(false);
        if(alpha > 0) {
            plotOptions.setFillColor(new Color(r, g, b, alpha));
        }
        return plotOptions;
    }

    private long getTime(String date) {
        return dateTimeFormat.parse(date).getTime();
    }

    @Override
    public Widget getWidget() {
        return vPanel;
    }

    @Override
    public void init() {
        currencyListBox.init();
        currencyListBox.addChangeHandler(this);
        getEventBus().addListener(BookSelectionChangedEvent.class, this);
        getEventBus().addListener(LoginEvent.class, this);
        getEventBus().addListener(AccountChangedEvent.class, this);
        getEventBus().addListener(TransactionChangedEvent.class, this);
//        updateChart();
    }

    public void handleEvent(AppEvent event) {
        if(event != null) {
            if(event instanceof LoginEvent) {
                updateChart();
            } else if(event instanceof BookSelectionChangedEvent) {
                updateChart();
            } else if(event instanceof AccountChangedEvent || event instanceof TransactionChangedEvent) {
                updateChart();
            }
        }
    }

    @Override
    public void onChange(CurrencyListBox listBox, Long[] values) {
        if(currencyListBox.equals(listBox)) {
            updateChart();
        }
    }
}
